#! /usr/bin/env python
# -*- coding:utf-8 -*-
import os, sys
cur_path = sys.path[0]
sys.path.append(cur_path)
import numpy as np
from scipy.optimize import minimize
import datetime
import model_regression as rm
t1 = datetime.datetime.now()
import load_info as load
import utils_data as util
import pandas as pd

"""
非线性规划算法根据能量需求做策略预测
结构：
    class Strategic_model     #策略模型
    def main 执行函数,输出策略预测结果
"""
class Strategic_model:
    """
    策略模型
    """
    def __init__(self, target, date,stgy_type=None, conn_file='mysql_conn.cnf'):
        self.target = target #能量需求值
        self.conn_file = conn_file
        self.date = date     #datetime
        ld = load.Device_load()
        gd = load.Graph_load()
        self.graph_id = gd.put_databydate(date.strftime("%Y-%m-%d"))  #图谱id
        if stgy_type is None:
            self.stgy_type = gd.load_strategy_type(self.graph_id)
        else:
            self.stgy_type = stgy_type

        self.worktime = gd.load_graph_worktime(self.graph_id) #图谱工作时间
        self.elec = ld.load_electricity(self.graph_id)  # 各时段电价dataframe
        ul = util.Device_util(self.graph_id, date)
        self.cap_dict = ul.cap_info()  #产能设备的属性
        self.relat_info = ul.first_choice(self.stgy_type) #当前图谱和策略类型下的工况
        self.work_state, self.slot_hours, self.intersect_hour = ul.work_state(self.relat_info["work_time_from"], self.relat_info[ "work_time_to"])  #各工况在各时段是否工作的标记,各时段小时数,各工况from和to设备工作时间交集的小时数
        self.relation_com =  ul.first_choice_com(self.stgy_type,self.relat_info) #当前图谱和策略类型下的并行工况
        print("self.stgy_type:",self.stgy_type)
    #定义系数矩阵
    def coef(self):
        """
        定义系数矩阵
        其中蓄能放能时，     e2/(host_effi_ratio*host_loss_rate*storage_loss_rate)-e2/(host_effi_ratio*host_loss_rate)+e2/storage_effi_ratio,
            供能放能时      e3/(host_effi_ratio*host_loss_rate),
        :return: coef :array, shape(1,工况数*时段数) float ,每个值表示当前工况和当前时段下每单位能量的成本
        """
        coef = np.array(self.elec["price"]) * self.work_state  # 电价乘以工作状态矩阵-获取工作状态下电价
        f_convert = self.cap_dict["conversion_rate"]  #产能设备的能效比(为蓄能设备蓄过能的)
        f_loss = self.cap_dict["loss_rate"]  # 产能设备的loss_rate(为蓄能设备蓄过能的)

        #按不同工况获取系数矩阵
        for i in range(self.relat_info.shape[0]):
            if self.relat_info["type_id_from"].values[i] == 2:  # 当蓄能放能时
                loss_i = self.relat_info["loss_rate"].values[i]  # 损失率
                convert_i = self.relat_info["conversion_rate_from"].values[i]  # 能效比
                coef[i] = coef[i] / (f_convert * (1 - f_loss) * (1 - loss_i)) - coef[i] / (f_convert * (1 - f_loss)) + \
                          coef[i] / convert_i  # 电价/(产能设备能效比*产能到蓄能损失率*蓄能到放能损失率）-电价/(产能设备能效比*产能到蓄能损失率）-电价/蓄能设备能效比
            else:
                loss_i = self.relat_info["loss_rate"].values[i]  # 损失率
                convert_i = self.relat_info["conversion_rate_from"].values[i]  # 能效比
                coef[i] = coef[i] / (convert_i * (1 - loss_i))  # 电价/(from设备能效比*损失率）
        coef = coef.flatten()
        return coef
    #目标分解到时段
    def split_demand(self):
        """
        目标分解，将日总能量需求分解到各时段中
        :return:
            slot_demand : array 形如[0 0 100 200 0]，每个子元素表示一个时段的能量需求
        """
        ####获取参数待用
        cap_power = self.cap_dict["power"]  # 产能设备的功率
        cap_hours = self.cap_dict["hours"]  # 产能设备的工作小时数
        cap_cap = self.cap_dict["cap"]  # 产能设备上限最大值
        target = self.target
        #### 如果日需求大于等于min(产能设备上限,产能设备功率*工作小时数的和),令日需求等于两者中较小的一个
        if target >= min(max(cap_cap), max(cap_power * cap_hours)):
            target = min(max(cap_cap), max(cap_power * cap_hours)) - 0.1
        ld = load.Device_load()
        lg = load.Graph_load()
        energy_code = ld.load_station_info()['energy_code'] #能量dev_code
        is_save = lg.load_is_save_graph(self.graph_id)      #上班前是否需要放能标志,0不需要,1需要
        if is_save==0:
            rms = rm.model_linear(self.graph_id, energy_code)  #八点前(图谱上班时间前)需求预测模型
            target_b8 = rms.linear_predict(target) #八点前(图谱上班时间前)需求
        else:
            target_b8 = 0

        ini_target = [target - target_b8, target_b8, 0]  #初始目标[上班目标,上班前目标,下班后目标]
        slot_start = [int(i.replace(":00", "")) if int(i.replace(":00", "")) < 23 else -1 for i in self.elec["start_time"]]  #每时段开始时间
        slot_end = [int(i.replace(":00", "")) for i in self.elec["end_time"]]  #每时段结束时间
        slot_hour = list(map(lambda x, y: x - y, slot_end, slot_start))  #每时段小时数
        type_slot = [1 if slot_end[i] <= self.worktime['start_work_time'] else 2 if slot_start[i] >= self.worktime[ 'end_work_time'] else 0 for i in range(len(slot_hour))]  # 时段类型标记 型如[1,1,0,0,2] 1表示上班前时段,2表示上班后时段,0表示上班时段
        type_hours = [sum([slot_hour[j] for j in range(len(type_slot)) if type_slot[j] == i]) for i in range(3)]  # 形如[9,9,6] 分别代表[上班时段小时数,上班前时段小时数,上班后时段小时数]
        type_max = [i * sum(cap_power) for i in type_hours]  # 形如,[3000,5000,2000]表示[上班时间放能上限,上班前放能上限,下班后放能上限]
        target_w0 = min(type_max[0], ini_target[0])  # 上班时间需求
        target_w1 = min(type_max[1], ini_target[1] + (ini_target[0] - target_w0))  # 上班前需求
        target_w2 = min(type_max[2], ini_target[2] + (ini_target[1] + (ini_target[0] - target_w0) - target_w1))  # 上班后需求
        rate_w0 = [slot_hour[i] / type_hours[0] if type_slot[i] == 0 else 0 for i in  range(len(slot_hour))]  # 上班时间各时段占总上班时间的比例,形如 [0,0,0.33,0.66,0]
        ##上班时间各时段的需求
        demand_w0 = np.array([i * target_w0 for i in rate_w0])  # 上班时间各时段的需求
        ##计算上班前各时段的需求
        rate_w1 = [sum(slot_hour[i] * cap_power) if type_slot[i] == 1 else 0 for i in range(len(slot_hour))]  # 上班前各时段的能量上限
        sort_end = [slot_end.index(j) for j in list(sorted([i for i in slot_end], reverse=True))]  # 各时段结束时间倒序排序后的时段索引
        rate_w1_sort = [rate_w1[i] for i in sort_end]
        demand_w1_ = []
        for i in rate_w1_sort:
            min_i = min(i, target_w1)
            demand_w1_.append(min_i)
            target_w1 = target_w1 - min_i

        demand_w1 = np.array([demand_w1_[j] for j in sort_end])  #上班前时间各时段的需求
        ##计算上班后各时段的需求
        rate_w2 = [sum(slot_hour[i] * cap_power) if type_slot[i] == 2 else 0 for i in range(len(slot_hour))]  #上班后各时段的能量上限
        demand_w2 = []  # 上班前时间各时段的需求
        for i in rate_w2:
            min_i = min(i, target_w2)
            demand_w2.append(min_i)
            target_w2 = target_w2 - min_i
        slot_demand = demand_w0 + demand_w1 + demand_w2
        print("各时段需求：",slot_demand)
        return slot_demand
    #定义矩阵相乘
    def multiply_matrix(self, coef, vari):
        """
        定义矩阵相乘
        :param coef: array [1,工况数*时段数] 系数
        :param vari: array [1,工况数*时段数] 变量
        :return:
            result: array [1,工况数*时段数]
        """
        result = 0
        for i in range(len(coef)):
            result += coef[i] * vari[i]
        return result
    #定义目标函数
    def objective(self, x):
        """
        定义目标函数
        :param x: array [1,工况数*时段数] 变量
        :return:
           obj :float ,目标函数的目标值,优化对象
        """
        a_values = self.coef()  ##策略模型关系对和电价时段生成策略模型矩阵
        coef = a_values
        vari = x
        obj = self.multiply_matrix(coef, vari)
        return obj
    #获取变量xi的取值范围bnds和初始值ini
    def bnds_and_ini(self):
        """
        获取变量的取值范围bnds，初始值ini
        :return:
           bnds: tuple ,变量的上下限,((x0下限,x0上限),(x1下限,x1上限)....(xn下限,xn上限))
           ini:  array shape(1,工况数*时段数) 变量初始值
        """
        ##获取参数
        from_powers = self.relat_info["power_from"].values  # 获取工况中的from设备的power

        ##计算bnds,变量取值范围
        bnd_max = []  # 存储取值范围的最大值
        for i in range(len(from_powers)):  # 对于每个工况
            bnd_max = bnd_max + list(
                from_powers[i] * self.slot_hours * self.work_state[i])  # 放能设备功率*各时段的工作小时数*各时段是否工作的标记
        bnds = tuple([(0, i) for i in bnd_max])  # bnds 变量取值范围

        ##计算ini,变量初始值
        ini = np.array([(i[1] - i[0]) / 5 for i in bnds])  # 目前是取max取值范围的5分之一
        return bnds, ini
    #获取限制条件函数集
    def con(self):
        """
        模型限制条件函数列表,分三种限制函数:
            1 各时段放能和大于等于各时段需求
            2 各from设备和to设备的上限,及蓄能和大于等于蓄放和
            3 各时段总工作时长限制
        :return:
            con :list ,每个子元素存储了一个限制条件的字典{"type": "ineq", "fun": function}
        """
        #获取各属性备用
        slot_demand = self.split_demand()  #分解后的各时段目标 list
        index_matrix = np.array([i for i in range(len(self.work_state.flatten()))]).reshape(self.relat_info.shape[0], len( self.slot_hours))  #index矩阵,存储未知数的索引

        con = []  # 存储限制条件
        #限制函数-1 各时段放能和>=各时段target
        type_to = [1 if i == 2 else 0 for i in self.relat_info["type_id_to"]]  # 标记当前关系对是蓄能模型还是放能模式，1是蓄能，0是放能
        slot_to = []  # 获取每个时段的放能索引 形如[[6,11],[7,12],[8,13],[9,14],[10,15]],子列表表示当前时段放能工况 对应的变量index
        for slot_i in range(len(self.slot_hours)):  #对于每个时段
            item_to = []
            for slot_j in range(len(type_to)):  #对于每个工况
                if type_to[slot_j] == 0:  #如果当前工况是放能模式
                    item_to.append(index_matrix[slot_j][slot_i])  #添加索引到放能索引的子列表
            slot_to.append(item_to)  #放能索引列表append放能索引子列表
        for w in range(len(slot_demand)):  #对于每个时段,放能和>=该时段需求
            con.append({"type": "ineq", "fun": lambda x, k=w: sum(x[slot_to[k]]) - slot_demand[k]})

        #限制函数-2 每个设备的能量限制
        iter_from = [list(i[1]) + [i[0]] for i in self.relat_info[["device_id_from", "cap_from", "power_from", "work_time_from"]].iterrows()]  # 取出每个工况from设备id,cap,power，work_time,行index
        iter_to = [list(i[1]) + [i[0]] for i in self.relat_info[["device_id_to", "cap_to"]].iterrows()]  # 取出每个工况to设备id,cap,power，行index
        from_set = dict([(i[0], i[1:-1]) for i in iter_from])  # 获取了from设备字典，key是from_id ,values 是【from_cap,power_from】
        to_set = dict([(i[0], i[1:-1]) for i in iter_to])  # 获取了to设备字典，key是to_id ,values 是【to_cap】
        from_index = dict([(i, self.relat_info[self.relat_info["device_id_from"] == i].index.tolist()) for i in list( from_set.keys())])  # 字典，key是from_id ,values 是list，存储了所有relat_info表中from_id等于当前from_id的行index
        to_index = dict([(i, self.relat_info[self.relat_info["device_id_to"] == i].index.tolist()) for i in list(to_set.keys())])  # 字典，key是to_id ,values 是list，存储了所有relat_info表中等于当前to_id的行index

        #2-1 to设备的限制条件,放能<=该设备的cap_to
        for to_key in to_index.keys():  # 对于每个to设备
            to_i = to_index[to_key]  # 获取该设备在relat_info表中的行index
            cap_i = to_set[to_key][0]  # 当前to设备的cap
            index_i = index_matrix[to_i].flatten()  # 获取当前设备对应的变量index matrix
            state_i = self.work_state[to_i].flatten()  # 获取当前设备对应的工作状态矩阵部分
            work_i = np.array([index_i[s_i] for s_i in range(len(index_i)) if state_i[s_i] == 1])  # 仅获取状态为工作的变量索引
            con.append({"type": "ineq", "fun": lambda x, k=work_i, cap=cap_i: cap - sum(x[k])})

        #2-2 from设备的限制条件,放能<=min(cap_from,from_power*工作小时数) ,其中工作小时数为min(设备工作时长,设备在各工况中与to设备交集的和)
        for f_key in from_index.keys():  # 对于每个from设备
            f_i = from_index[f_key]  # 获取该设备在relat_info表中的行index
            f_hour_sum = sum([self.intersect_hour[hour_i] for hour_i in f_i])  # 当前设备在所有工况中的总工作小时数（在与to设备工作时间取交集的情况下）
            time_item = [int(time_i) for time_i in from_set[f_key][2].split("-")]  # 当前from设备的工作开始结束时间
            time_item = [-1 if i_2 == 0 and time_item[i_2] == 23 else time_item[i_2] for i_2 in range(2)]
            f_hours = time_item[1] - time_item[0]  # 当前from设备的工作时长
            f_cap = from_set[f_key][0]  # 当前from设备的cap
            f_power = from_set[f_key][1]  # 当前from设备的power
            index_i = index_matrix[f_i].flatten()  # 获取当前设备对应的变量index matrix
            min_up = min(f_cap, f_power * min(f_hour_sum, f_hours))  # 当前设备上限
            con.append({"type": "ineq", "fun": lambda x, k1=index_i, k2=min_up: k2 - sum(x[k1])})

        #2-3 每个蓄能设备的总放能要小于等于总蓄能
        save_set = list(set(list( self.relat_info[self.relat_info["type_id_from"] == 2]["device_id_from"].tolist())))  # 蓄能设备的id集合,形如[20,3]
        release_index = [self.relat_info[self.relat_info["device_id_from"] == i].index.tolist() for i in save_set]  # 当工况为蓄放，各蓄能设备的行index [[9,11].[10,12]],每个子列表对应save_set中的每个设备
        save_index = [self.relat_info[self.relat_info["device_id_to"] == i].index.tolist() for i in save_set]  # 当工况为蓄蓄，各蓄能设备的行index [[0,1].[2,3]],每个子列表对应save_set中的每个设备
        release_i = [list(sum([index_matrix[j].tolist() for j in i], [])) for i in release_index]  # 当工况为蓄放，蓄能设备在变量x矩阵中的索引值,形如[[10, 11, 12, 13, 14],[16, 17, 18, 19, 20]]
        save_i = [list(sum([index_matrix[j].tolist() for j in i], [])) for i in save_index]  # 当工况为蓄蓄，蓄能设备在变量x矩阵中的索引值,形如[[10, 11, 12, 13, 14],[16, 17, 18, 19, 20]]
        for item_dev_id in range(len(save_set)):  # 对于每个蓄能设备,总蓄放<=总蓄蓄
            con.append({"type": "ineq", "fun": lambda x, k=item_dev_id: sum(x[save_i[k]]) - sum(x[release_i[k]])})

        #限制函数-3 每个时段的总工时限制
        ul = util.Device_util(self.graph_id, self.date)
        para_mark, para_time = ul.parallel_mark(self.relation_com,self.relat_info)  # 得到工况的并行标记，如果无并行为0，并行为1 [0,1,1] #C
        from_power = self.relat_info["power_from"].tolist()  # 得到工况的from_power #P
        row_single = [item_0 for item_0 in range(len(para_mark)) if para_mark[item_0] == 0]  # c0,形如[2,3],存储了工况中不存在并行关系的设备行索引
        row_comb = [[j for j, x in enumerate(para_mark) if x == i] for i in list(set(para_mark)) if  i != 0]  # ct,形如[[0,1],[4,5]] 存储了工况中各并行关系的索引，每个子列表代表一组并行关系
        comb_time = [k[0] for k in [[para_time[j] for j in i] for i in row_comb]]  # 二维列表,每个子列表代表一个并行工况的开始,结束时间形如[[4,17],[10,17]]
        el_start = [int(i.split(":")[0]) if int(i.split(":")[0]) < 23 else -1 for i in  self.elec["start_time"]]  # 电价时段的开始时间
        el_end = [int(i.split(":")[0]) for i in self.elec["end_time"]]  # 电价时段的结束时间
        comb_slot_mark = []  # [[0, 1, 1, 1, 0],[0, 1, 1, 1, 0]] 每个子列表,表示每个并行工况各时段是否并行的标记
        for item_r in comb_time:
            item_comb = []
            for item_t in range(len(el_start)):
                if item_r[0] <= el_start[item_t] and item_r[1] >= el_end[item_t]:
                    item_comb.append(1)
                else:
                    item_comb.append(0)
            comb_slot_mark.append(item_comb)
        for slot_i in range(len(self.slot_hours)):  # 对于每个时段
            slot_index = index_matrix.T[slot_i].tolist()  # 每时段的index_matrix #I [0,5,10]
            slot_state = self.work_state.T[slot_i].tolist()  # 每时段的work_state #W [1,1,0]
            single_i = [slot_index[item_i] for item_i in row_single]     #形如[9,14] 当前时段下，对应 row_single行索引的变量x在矩阵中的真实索引值
            comb_i_ini = [[slot_index[j] for j in i] for i in row_comb]  #形如[[5,15],[1,6]] 当前时段下，对应row_comb行索引的变量x在矩阵中的真实索引值
            single_p = [from_power[item_i] for item_i in row_single]     #形如[550,700] 当前时段下，对应row_single行索引的工况from设备功率
            comb_p_ini = [[from_power[j] for j in i] for i in row_comb]  #形如[[550,700],[550,550]] 当前时段下，对应row_comb行索引的工况from设备功率
            single_w = [slot_state[item_i] for item_i in row_single]     #形如[1,1] 当前时段下，对应row_single行索引的工作状态标识,1为工作,0为不工作
            comb_w_ini = [[slot_state[j] for j in i] for i in row_comb]  #形如[[1,1],[1,0]] 当前时段下，对应row_comb行索引的工作状态标识,1为工作,0为不工作
            comb_i = []; comb_p = [];comb_w = []
            for len_com in range(len(comb_slot_mark)):  #
                if comb_slot_mark[len_com][slot_i] == 0:
                    single_i += comb_i_ini[len_com]
                    single_p += comb_p_ini[len_com]
                    single_w += comb_w_ini[len_com]
                else:
                    comb_i.append(comb_i_ini[len_com])
                    comb_p.append(comb_p_ini[len_com])
                    comb_w.append(comb_w_ini[len_com])
            if len(comb_i) > 0:
                con.append({"type": "ineq", "fun": lambda x,
                                                          k=slot_i,
                                                          i_single=single_i,
                                                          p_single=single_p,
                                                          w_single=single_w,
                                                          i_comb=comb_i,
                                                          p_comb=comb_p,
                                                          w_comb=comb_w:
                self.slot_hours[k] -sum( x[i_single] / p_single * w_single ) - sum([max(x[i_comb[c]] / p_comb[c] * w_comb[c] ) for c in range(len(i_comb))])})
            else:
                con.append({"type": "ineq","fun": lambda x,
                                                         k=slot_i,
                                                         i_single=single_i,
                                                         p_single=single_p,
                                                         w_single=single_w:
                                                         self.slot_hours[k] - sum( x[i_single] / p_single * w_single )})
        return con
    #策略模型
    def strate_model(self):
        """
        策略模型主运行函数,根据输入的能量及日期预测策略
        :return:
           res:  array ,shape(1,工况数*时段数)  策略预测结果矩阵
        """
        #获取各种属性备用
        cons = self.con()  # 限制条件
        bnds, ini = self.bnds_and_ini()  # 变量取值范围、变量初始化值
        res = minimize(self.objective, ini, method='SLSQP', bounds=bnds, constraints=cons)
        print("message:", res.message)
        res_flatten = res.x  # 计算结果矩阵1维表示
        res = np.reshape(res_flatten, (self.work_state.shape[0],len(self.slot_hours))) #计算结果矩阵二维表示
        # print("=================================开始=====================================")
        # print("x-输出能量-23_7   7_8   8_11   11_17    17_23--->")
        # print([round(i) for i in res_flatten][:5])
        # print([round(i) for i in res_flatten][5:10])
        # print([round(i) for i in res_flatten][10:15])
        # try:
        #     print([round(i) for i in res_flatten][15:20])
        #     print([round(i) for i in res_flatten][20:25])
        # except IndexError:
        #     pass
        return res
    #计算结果格式转换
    def strate_format(self, res):
        """
        策略预测结果矩阵转换为字典
        :param res: array,策略预测结果
        :return: strate_dict:策略字典:key是（from_device_id,to_device_id）,value 是[...] 17时至17时各小时power
        """
        gd = load.Graph_load()
        graph_id = self.graph_id
        ul = util.Device_util(graph_id)
        day_define = ul.day_define()
        #策略模型结果转换
        power_f = self.relat_info["power_from"].values  # from设备的功率 []*3
        type_f = self.relat_info["type_id_from"].values  # from设备的功率 []*3
        type_t = self.relat_info["type_id_to"].values  # from设备的功率 []*3
        save_save = [i for i in range(len(type_f)) if type_f[i] == 1 and type_t[i] == 2]  # 蓄能工况的index [0,1]
        save_out = [i for i in range(len(type_f)) if type_f[i] == 2 and type_t[i] == 3]  # 蓄放工况的index [2]
        host_out = [i for i in range(len(type_f)) if type_f[i] == 1 and type_t[i] == 3]  # 主机直供的index [3,4]
        slot_start = [int(i.replace(":00", "")) if int(i.replace(":00", "")) < 23 else -1 for i in  self.elec["start_time"]]  # 每时段开始时间
        slot_end = [int(i.replace(":00", "")) for i in self.elec["end_time"]]  # 每时段结束时间
        slot_hour = list(map(lambda x, y: x - y, slot_end, slot_start))  # 每时段小时数
        dev_from = self.relat_info["device_from"].values
        dev_to = self.relat_info["device_to"].values
        work_start, work_end = gd.load_graph_worktime(graph_id)["start_work_time"], gd.load_graph_worktime(graph_id)[  "end_work_time"]  # 图谱下开始结束时间 int 8,17

        strate_dict = {} #保存字典
        for i in range(len(slot_hour)):  # 对于每个时段
            if slot_end[i] <= work_start:  # 时段结束时间小于等于上班开始时间,即上班前时段
                res_i = res.T[i]  # 当前时段各工况的预测结果 [2750,1280,0]
                hour_i = res_i / power_f  # [0.3,3,7] 表示每工况实际全功率小时数
                hour_i = [round(i) for i in hour_i]  # [0.3,3,7] 表示每工况实际全功率小时数
                if 0 < sum(hour_i) <= slot_hour[i]:  # 如果全功率放能和小于该时段小时数
                    type_order =host_out+ save_out+save_save   #工况执行顺序 [2,1,0]，从时段结束时间倒序全功率执行
                    res_i = res.T[i]  # 当前时段各工况的预测结果 [2750,1280,0]
                    slot_end_item = slot_end[i]
                    for j in type_order:  # 对于每个工况
                        value = res_i[j]  # 总能量值
                        if value > 1 and int(round(value / power_f[j])) > 0:  # 如果能量值大于0
                            power = power_f[j]  # from_power
                            hour_use = int(round(value / power))  # 实际用时四舍五入取整
                            index_start = day_define.index( slot_end_item - hour_use if (slot_end_item - hour_use) >= 0 else 24 + ( slot_end_item - hour_use))  # 开始时间在day_define中的索引
                            index_end = day_define.index(slot_end_item - 1 if (slot_end_item - 1) >= 0 else 24 + ( slot_end_item - 1))  # 结束时间在day_define中的索引
                            key_item = (dev_from[j], dev_to[j])  # 新建字典的key,tuple（device_from,device_to）
                            value_item = np.array([power if l >= index_start and l <= index_end else 0 for l in  range(len(day_define))]).astype( 'float64')  # 对应day_define列表的,1*24的np.array;表示从17时至转日16时各小时的输出功率
                            if key_item in strate_dict.keys():  # 将{"工况名：[power,......,power]"}写入字典
                                strate_dict[key_item] += value_item
                            else:
                                strate_dict[key_item] = value_item
                            slot_end_item = slot_end_item - hour_use  # 新结束时间
                else:
                    type_order = save_save + save_out + host_out  # 工况执行顺序 [2,1,0]
                    power_list = res_i / slot_hour[i]
                    for j in type_order:  # 对于每个工况
                        value = res_i[j]  # 总能量值
                        if value > 0 and power_list[j] > 1:  # 如果能量值大于0
                            power = int(power_list[j])  # from_power
                            index_start = day_define.index(slot_start[i] if slot_start[i]>0 else 24+slot_start[i])  # 开始时间在day_define中的索引
                            index_end = day_define.index(slot_end[i] - 1)  # 结束时间在day_define中的索引
                            key_item = (dev_from[j], dev_to[j])  # 新建字典的key,tuple（device_from,device_to）
                            value_item = np.array([power if l >= index_start and l <= index_end else 0 for l in  range(len(day_define))]).astype('float64')  # 对应day_define列表的,1*24的np.array;表示从17时至转日16时各小时的输出功率
                            if key_item in strate_dict.keys():  # 将{"工况名：[power,......,power]"}写入字典
                                strate_dict[key_item] += value_item
                            else:
                                strate_dict[key_item] = value_item
            else:  #上班时段或下班时段
                res_i = res.T[i]  # 当前时段各工况的预测结果 [2750,1280,0]
                hour_i = res_i / power_f  # [0.3,3,7] 表示每工况实际全功率小时数

                if 0 < sum(hour_i) < slot_hour[i]:  # 如果全功率放能和小于该时段小时数
                    type_order = save_save + save_out + host_out  # 工况执行顺序 [2,1,0]，从时段结束时间倒序全功率执行
                    slot_start_item = slot_start[i]
                    hour_use_list = [int(round(k / sum(hour_i) * slot_hour[i])) for k in hour_i]  # [2,3,5] 按用时比例均摊后的小时数
                    power_list = [res_i[item_d] / hour_use_list[item_d] if hour_use_list[item_d] > 0 else 0 for item_d  in range(len(res_i))]
                    for j in type_order:  # 对于每个工况
                        value = res_i[j]  # 总能量值
                        if value > 0 and power_list[j] > 1:  # 如果能量值大于0
                            power = int(power_list[j])  # from_power
                            hour_use = hour_use_list[j]  # 实际用时四舍五入取整
                            index_start = day_define.index( slot_start_item if slot_start_item > 0 else 24 + slot_start_item)  # 开始时间在day_define中的索引
                            index_end = day_define.index(slot_start_item + hour_use - 1 if (slot_start_item + hour_use - 1) > 0 else 24 + ( slot_start_item + hour_use - 1))  # 结束时间在day_define中的索引
                            key_item = (dev_from[j], dev_to[j])  # 新建字典的key,tuple（device_from,device_to）
                            value_item = np.array([power if l >= index_start and l <= index_end else 0 for l in range(len(day_define))]).astype( 'float64')  # 对应day_define列表的,1*24的np.array;表示从17时至转日16时各小时的输出功率
                            if key_item in strate_dict.keys():  # 将{"工况名：[power,......,power]"}写入字典
                                strate_dict[key_item] += value_item
                            else:
                                strate_dict[key_item] = value_item
                            slot_start_item = slot_start_item + hour_use  # 新结束时间
                else:
                    type_order = save_save + save_out + host_out  # 工况执行顺序 [2,1,0]
                    power_list = res_i / slot_hour[i]
                    for j in type_order:  # 对于每个工况
                        value = res_i[j]  # 总能量值
                        if value > 0 and power_list[j] > 1:  # 如果能量值大于0
                            power = int(power_list[j])  # from_power
                            index_start = day_define.index(slot_start[i])  # 开始时间在day_define中的索引
                            index_end = day_define.index(slot_end[i] - 1)  # 结束时间在day_define中的索引
                            key_item = (dev_from[j], dev_to[j])  # 新建字典的key,tuple（device_from,device_to）
                            value_item = np.array([power if l >= index_start and l <= index_end else 0 for l in  range(len(day_define))]).astype('float64')  # 对应day_define列表的,1*24的np.array;表示从17时至转日16时各小时的输出功率
                            if key_item in strate_dict.keys():  # 将{"工况名：[power,......,power]"}写入字典
                                strate_dict[key_item] += value_item
                            else:
                                strate_dict[key_item] = value_item
        if self.stgy_type == 1003:
            strate_dict = ul.economics_first(day_define, strate_dict, self.relation_com)
        return strate_dict
#执行函数
def main(target, date,stgy_type):
    """
    根据需求和日期预测策略
    :param target: int,能量需求
    :param date: datetime
    :return: res_dict:策略字典
             strat_list:策略列表
    """
    sm = Strategic_model(target, date,stgy_type)
    res = sm.strate_model()
    res_dict = sm.strate_format(res)  #预测结果字典,预测结果列表
    return res_dict
if __name__ == "__main__":
    """
    策略模型函数--目前限制条件没有尽量连续这个功能
    """
    target= 4049  # 日能量需求
    date = '20220308'
    date = datetime.datetime(int(date[:4]), int(date[4:6]), int(date[6:]))
    stgy_type = 1003
    res_dict = main(target, date,stgy_type)
    print("---------------预测工况-------------->")
    for item in res_dict.keys():
        print("工况:",item,list(res_dict[item]))

    print("---------------list------------->")
    strat_list = util.Device_util(13).strategy_to_list_mean(res_dict, 159, date)
    for i in strat_list:
        print(i)

